/*
 * hash.h
 *
 *  Created on: Oct 3, 2012
 *      Author: hammer
 */

class Hash_table {
  vector< vector< vector<float> >  > list;
  vector<int> memory;
  int hash_function(vector<int>);
  int N_table,N_polymer,DIM;
  bool touching(float sphere_size,vector<float> pos1,vector<float> pos2);
  bool check_sublist(int n,vector<float> pos,float l);
  void recursive_check(vector<float>& pos,vector<int>& pos1,vector<int>& add,int level,int * check,float l);
public:
  Hash_table(int n,int d);
  void add_pos(vector<float>);
  void print();
  void clear();
  bool check_SA(Polymer,float l);
};

Hash_table::Hash_table(int n,int d) {
  DIM=d;
  N_polymer=n;
  N_table=20*N_polymer;
  list.resize(N_table);
}

int Hash_table::hash_function(vector<int> pos) {
  int i;
  float a,h;
  h=0;
  for (i=0;i<DIM;i++) {
    a=pow(float(abs(pos[i])*1000),float((i+1))/float((DIM+1)));
    h=h+a;
  }
  h = fmod(h+1,N_table);
  if (h>N_table-1) return 0;
  else return h;
}

void Hash_table::add_pos(vector<float> pos) {
  vector<int> pos1;
  int i,h,x;
  for (i=0;i<DIM;i++) {
    x=floor(pos[i]);
    pos1.push_back(x);
  }
  h=hash_function(pos1);
  list[h].push_back(pos);
  memory.push_back(h);
}

void Hash_table::clear() {
  int i;
  vector<float> pos;
  if (memory.size()>0) {
    for (i=0;i<memory.size();i++) {
      list[memory[i]].clear();
    }
    memory.clear();
  }
}

void Hash_table::print() {
  int i,j,k;
  cout<< "memory: ";
  for (i=0;i<memory.size();i++) {
    cout << memory[i] << " ";
  }
  cout << "\n";
  for (i=0;i<N_table;i++) {
    if (list[i].size()>0) {
      cout << i << ":\n";
      for (k=0;k<list[i].size();k++) {
	for (j=0;j<DIM;j++) {
	  cout<< list[i][k][j]<<"\t";
	}
	cout<< "\n";
      }
    }
  }
}

bool Hash_table::touching(float sphere_size,vector<float> pos1,vector<float> pos2) {
  float r=0;
  int i;
  for (i=0;i<DIM;i++) {
    r= r + (pos1[i]-pos2[i])*(pos1[i]-pos2[i]);
  }
  r=sqrt(r);
  if (r<sphere_size) return 1;
  else return 0;
}

bool Hash_table::check_sublist(int n,vector<float> pos,float l){
	int i,s;//,j;
	s=list[n].size();
	for (i=0;i<s;i++) {
		if (touching(l,pos,list[n][i])) {
			//cout << "contact:\n" << pos[0];
			//for (j=1;j<DIM;j++) {
			//	cout<< ", " <<  pos[j];
			//}
			//cout <<  "\n";
			//cout << list[n][i][0];
			//for (j=1;j<DIM;j++) {
			//	cout<< ", " <<  list[n][i][j];
			//}
			//cout <<  "\n";
			return 0;
		}
	}
	return 1;
}

void Hash_table::recursive_check(vector<float>& pos,vector<int>& pos1,vector<int>& add,int level,int * check,float l) {
	int i;
	if (level==DIM) {
		vector<int> value;
		value.resize(DIM);
		for (i=0;i<DIM;i++) {
			value[i]=pos1[i]+add[i];
		}
		//cout << "checking " << value[0] << ", " << value[1] << ", " << value[2] << "\n";
		if (!check_sublist(hash_function(value),pos,l)) *check=0;
	}
	else for (i=-1;i<2;i++) {
		add[level]=i;
		recursive_check(pos,pos1,add,level+1,check,l);
	}
}

bool Hash_table::check_SA(Polymer poly1,float l) {
	int i,j,n;
	int check;
	vector<float> pos;
	vector<int> pos1;
	vector<int> add;
	pos.resize(DIM);
	pos1.resize(DIM);
	add.resize(DIM);
	check=1;
	add_pos(poly1.get_pos(0));
	n=poly1.get_length();
	for (i=1;i<n;i++) {
		pos=poly1.get_pos(i);
		for (j=0;j<DIM;j++) {
			pos1[j]=floor(pos[j]);
		}
		//cout << "position: " << pos[0] <<", " << pos[1] << ", " << pos[2] << "\n";
		//cout << "int position: " << pos1[0] <<", " << pos1[1] << ", " << pos1[2] << "\n";
		recursive_check(pos,pos1,add,0,&check,2*l);
		if (check==0) {
			clear();
			return 0;
		}
		add_pos(pos);
	}
	clear();
	return 1;
}
